package app.keter.portal.security;

import app.keter.portal.config.Services;
import app.keter.portal.security.core.UserDetailsImpl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.keter.framework.web.result.JSONResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 *   Security领域模型： 用于处理针对当前应用的用户相关逻辑
 */
@Service
public class SecurityService implements Serializable {

    private static final Logger logger = LoggerFactory.getLogger(SecurityService.class);

    @Autowired
    Services services;

    private  String TOKEN_API;

    @Autowired void init() {
        TOKEN_API = services.getCommon() + "/api/v1/security/token/";
    }

    @Autowired
    RestTemplate restTemplate;

    public UserDetails findUserDetailsByUsername(String username) {
        JSONResult result = restTemplate.getForObject(services.getCommon() + "/api/v1/security/username/{1}", JSONResult.class, username);
        if (result.isError() || result.isEmpty()) {
            return null;
        }
        UserDetails userDetails = new UserDetailsImpl(
                result.object().getString("id"),
                username,
                result.object().getString("password"),
                result.object().getString("email"),
                result.object().getJSONArray("authorities")
        );
        logger.info("roles:{}", userDetails.getAuthorities());
        return userDetails;
    }

    /**
     * 根据用户身份信息生成token
     * @param userDetails
     * @return
     */
    public String toToken(UserDetailsImpl userDetails) {
        return restTemplate.postForObject(TOKEN_API + "/generate", userDetails, JSONResult.class).ensure().data();
    }

    LoadingCache<String, UserDetailsImpl> userDetailsLoadingCache = CacheBuilder.newBuilder()
            .maximumSize(1000)
            .expireAfterAccess(10, TimeUnit.MINUTES)
            .build(new CacheLoader<String, UserDetailsImpl>() {
                @Override
                public UserDetailsImpl load(String token) {
                    logger.info("我被调用了");
                    // 这里是key根据实际去取值的方法
                    return getUserDetails(token);
                }
            });

    /**
     * 从token中解析用户对象
     * @param token
     * @return
     */
    private UserDetailsImpl getUserDetails(String token) {
        JSONObject result = restTemplate.getForObject(TOKEN_API + "/{1}/user", JSONResult.class, token).ensure("token 认证失败！").data();
        return new UserDetailsImpl(
                result.getString("id"),
                result.getString("username"),
                "",
                result.getString("email"),
                getAuthoritiesArray((List<Map>) result.get("authorities"))
        );
    }

    /**
     * 根据token生成用户信息：每次携带认证token的请求都需要调用
     * @param token
     * @return
     */
    public UserDetailsImpl fromToken(String token) throws ExecutionException {
        return userDetailsLoadingCache.get(token);
    }

    /**
     * 将list类型的authorities转换成JsonArray：
     *
     * @param authorities
     * @return
     */
    private JSONArray getAuthoritiesArray(List<Map> authorities) {
        JSONArray array = new JSONArray();
        authorities.stream().forEach(
                o -> array.add(o.get("authority"))
        );
        return array;
    }

    public JSONResult refresh(String token) {
        return restTemplate.getForObject(TOKEN_API + "/{1}/refresh", JSONResult.class, token);
    }

    public JSONResult refreshResponse(String token) {
        JSONResult response =  restTemplate.getForObject(TOKEN_API + "/{1}/refresh2", JSONResult.class, token);
        logger.info("response json:{}", JSON.toJSONString(response));
        return  response;
    }
}

